#pragma once
#include <iostream>
#include <string>
#include <stdarg.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;

#define CSTR(str) (str).c_str()

enum LogLevel
{
    Debug,
    Info,
    Error,
    Abnormal
};

class Log
{
private:
    enum Style
    {
        Screen,
        OneFile,
        ClassFile
    };

    const Style defaultstyle = Screen;
    const string defaultfilename = "log";

    string LogLevelToString(LogLevel level)
    {
        switch (level)
        {
        case Debug:
            return "Debug";
            break;
        case Info:
            return "Info";
            break;
        case Abnormal:
            return "Abnormal";
            break;
        default:
            return "";
            break;
        }
    }

    string TimeToString()
    {
        time_t cur = time(nullptr);
        struct tm *curtime = localtime(&cur);
        char timebuffer[128];
        snprintf(timebuffer, sizeof(timebuffer), "%d-%d-%d %d:%d:%d",
                 curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday,
                 curtime->tm_hour, curtime->tm_min, curtime->tm_sec);

        return timebuffer;
    }

    void WriteInfoToOneFile(const string &level, const string &info)
    {
        string filename = _filename;
        filename += '.';
        filename += level;

        int fd = open(CSTR(filename), O_CREAT | O_WRONLY | O_APPEND, 0664);
        write(fd, CSTR(info), info.size());
        close(fd);
    }

    void WriteInfoToClassFile(const string &level, const string &info)
    {
        WriteInfoToOneFile(level, info);
    }

    void WriteLog(const string &level, const string &info)
    {
        switch (_style)
        {
        case Screen:
            cout << info;
            break;
        case OneFile:
            WriteInfoToOneFile("all", info);
            break;
        case ClassFile:
            WriteInfoToClassFile(level, info);
            break;
        default:
            break;
        }
    }

    void LogMessage(LogLevel level, const char *format, va_list &args)
    {
        string leftbuffer;
        string levelstr = LogLevelToString(level);
        leftbuffer += '[' + levelstr + ']';
        leftbuffer += '[' + TimeToString() + ']';
        leftbuffer += '[' + to_string(getpid()) + ']';

        char rightbuffer[1024];
        vsnprintf(rightbuffer, sizeof(rightbuffer), format, args);
        va_end(args);

        string info;
        info += leftbuffer;
        info += rightbuffer;

        WriteLog(levelstr, info);
    }

    Log() : _style(defaultstyle), _filename(defaultfilename) {}
    Log(const Log &) = delete;
    Log &operator=(const Log &) = delete;

    void Enable(Style style)
    {
        _style = style;
    }

public:
    void operator()(LogLevel level, const char *format, ...)
    {
        va_list args;
        va_start(args, format);
        LogMessage(level, format, args);
    }

    ~Log() {}

    static Log &GetLog()
    {
        return log;
    }

private:
    Style _style;
    string _filename;

    class Conf
    {
    public:
        Conf()
        {
            log.Enable(Screen);
        }

        ~Conf()
        {
        }

        static Conf conf;
    };

    static Log log;
};

Log Log::log;
Log::Conf Log::Conf::conf;

#define LOG Log::GetLog()
